package com.ytjj.qmyx.admin.service.data.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.ytjj.common.constants.RedisConstants;
import com.ytjj.common.dto.mqdto.OrderRecordDto;
import com.ytjj.common.service.RedisService;
import com.ytjj.qmyx.admin.constants.data.CollectionConstant;
import com.ytjj.qmyx.admin.constants.data.MongoCollectionNameConstants;
import com.ytjj.qmyx.admin.constants.data.PathCodeConstant;
import com.ytjj.qmyx.admin.dao.OrderStatisticsMapper;
import com.ytjj.qmyx.admin.eunm.ActivityTypeNameCode;
import com.ytjj.qmyx.admin.eunm.DateTypeEnums;
import com.ytjj.qmyx.admin.eunm.SpecialClickEnums;
import com.ytjj.qmyx.admin.eunm.TimeEnums;
import com.ytjj.qmyx.admin.fun.TimeInterfaceFun;
import com.ytjj.qmyx.admin.model.data.mongo.ClickStatistics;
import com.ytjj.qmyx.admin.model.data.mongo.PageStatistics;
import com.ytjj.qmyx.admin.model.data.mongo.ProductStatistics;
import com.ytjj.qmyx.admin.model.data.response.*;
import com.ytjj.qmyx.admin.model.dto.TimeDto;
import com.ytjj.qmyx.admin.model.response.ProductDataResponse;
import com.ytjj.qmyx.admin.model.response.statistics.OrderStatisticsResponse;
import com.ytjj.qmyx.admin.model.response.statistics.RedPacketResponse;
import com.ytjj.qmyx.admin.service.data.ProductStatisticsService;
import com.ytjj.qmyx.admin.utils.CalendarUtil;
import com.ytjj.qmyx.admin.utils.DateUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.mapreduce.GroupBy;
import org.springframework.data.mongodb.core.mapreduce.GroupByResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 *
 *  *
 **/
@Service
public class ProductStatisticsServiceImpl implements ProductStatisticsService {

    @Resource
    private MongoTemplate mongoTemplate;
    @Resource
    private OrderStatisticsMapper orderStatisticsMapper;
    @Resource
    private RedisService redisService;


    @Override
    public ProductStatisticsResponse getProductStatisticList(String dateType, String platform, String channel) {
        // 从redis取出商品记录
        String s = redisService.get(RedisConstants.PRODUCT_STATISTICS + platform + dateType);
        if (StringUtils.isNotEmpty(s)) {
            return JSONObject.parseObject(s, ProductStatisticsResponse.class);
        }
        Criteria criteria = this.commonCriteria(platform, channel, dateType, null);
        // 商品浏览量
        long productBrowseNum = mongoTemplate.count(new Query(criteria), ProductStatistics.class);
        // 商品访问人数
        int productVisitNum = this.aggregateByUuid(criteria, CollectionConstant.PRODUCT).size();
        // 店铺浏览量
        Criteria bankerSiteBrowseCriteria = criteria.and("pathCode").is(PathCodeConstant.BRAND_SITE_PAGE);
        long bankerBrowseNum = mongoTemplate.count(new Query(bankerSiteBrowseCriteria), PageStatistics.class);
        // 店铺访问人数
        int bankerVisitNum = this.aggregateByUuid(bankerSiteBrowseCriteria, CollectionConstant.PAGE).size();
        DateResponse time = this.getTime(dateType, platform, channel, null);
        // 订单统计
        OrderResponse orderResponse = orderStatisticsMapper.getOrderNum(time);
        // 商品销量排行
        List<ProductResponse> orderRank = this.getProductRank(time);
        // 商品销售量和成交额
        List<Object> orderDataList = this.getOrderData(dateType, time);
        // 专区点击量
        List<SpecialClickResponse> productSpecialClickNum = this.getProductSpecialClickNum(time);
        ProductStatisticsResponse productStatisticsResponse = new ProductStatisticsResponse();
        productStatisticsResponse.setProductBrowseNum(productBrowseNum);
        productStatisticsResponse.setProductVisitNum(Long.valueOf(productVisitNum));
        productStatisticsResponse.setBankerBrowseNum(bankerBrowseNum);
        productStatisticsResponse.setBankerVisitNum(Long.valueOf(bankerVisitNum));
        productStatisticsResponse.setCreateOrderNum(orderResponse.getPayOrder() + orderResponse.getWaitPayOrder());
        productStatisticsResponse.setWaitPayOrderNum(orderResponse.getWaitPayOrder());
        productStatisticsResponse.setPayOrderNum(orderResponse.getPayOrder());
        productStatisticsResponse.setProductRank(orderRank);
        productStatisticsResponse.setOrderData(orderDataList);
        productStatisticsResponse.setProductSpecialList(productSpecialClickNum);
        this.redisConfig(dateType, platform, productStatisticsResponse);
        return productStatisticsResponse;
    }

    /**
     * @param platform
     * @param channel
     * @return
     */

    @Override
    public List<ProductDataResponse> getTodayProductData(String platform, String channel) {
        Criteria criteria = this.commonCriteria(platform, channel, DateTypeEnums.DAY.name(), null);
        List<ProductDataResponse> productDataResponseList = this.aggregateByProductName(criteria, null);
        System.out.println(JSONObject.toJSONString(productDataResponseList, true));
        for (ProductDataResponse productDataResponse : productDataResponseList) {
            Integer productId = productDataResponse.getProductId();
            ProductDataResponse sqlProductData = orderStatisticsMapper.getProductDataList(productId, channel, platform, null, null);
            ProductDataResponse allPrice = orderStatisticsMapper.getPrice(productId);
            // 成本
            BigDecimal costPrice = allPrice.getCostPrice();
            // 实际购买价
            BigDecimal price = allPrice.getPrice();
            if (sqlProductData != null) {
                productDataResponse.setCostPrice(costPrice);
                productDataResponse.setPrice(price);
                productDataResponse.setProductId(productId);
                productDataResponse.setPayOrderNum(sqlProductData.getPayOrderNum());
                productDataResponse.setUnPayOrderNum(sqlProductData.getUnPayOrderNum());
                productDataResponse.setTotalOrderNum(sqlProductData.getTotalOrderNum());
                productDataResponse.setActivityType(ActivityTypeNameCode.getName(productDataResponse.getActivityType()));
            }
        }
        return productDataResponseList;
    }

    @Override
    public List<ProductDataResponse> getProductDataByDay(String platform, String channel, String dayTime) {
        Criteria criteria = this.commonCriteria(platform, channel, DateTypeEnums.TIME.name(), dayTime);
        List<ProductDataResponse> productDataResponseList = this.aggregateByProductName(criteria, dayTime);
        LocalDate baseTime = LocalDate.parse(dayTime, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        Date startTime = CalendarUtil.dateFormatParse(baseTime);
        Date endTime = CalendarUtil.getEndTime(startTime);
        for (ProductDataResponse productDataResponse : productDataResponseList) {
            Integer productId = productDataResponse.getProductId();
            ProductDataResponse sqlProductData = orderStatisticsMapper.getProductDataList(productId, channel, platform, startTime, endTime);
            ProductDataResponse allPrice = orderStatisticsMapper.getPrice(productId);
            // 成本
            BigDecimal costPrice = allPrice.getCostPrice();
            // 实际购买价
            BigDecimal price = allPrice.getPrice();
            // 实际订单数
            Integer realOrderNum = sqlProductData.getRealOrderNum();
            // uv
            Integer uv = productDataResponse.getUv();
            // 支付订单数
            Integer payOrderNum = sqlProductData.getPayOrderNum();
            // 订单率 实际订单数/uv
            BigDecimal orderRatio = uv == 0 ? BigDecimal.ZERO : BigDecimal.valueOf(realOrderNum)
                    .divide(BigDecimal.valueOf(uv), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
            // 支付率 （支付订单/实际订单数）
            BigDecimal payRatio = realOrderNum == 0 ? BigDecimal.ZERO : BigDecimal.valueOf(payOrderNum)
                    .divide(BigDecimal.valueOf(realOrderNum), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
            if (sqlProductData != null) {
                productDataResponse.setProductId(productId);
                productDataResponse.setCostPrice(costPrice);
                productDataResponse.setPrice(price);
                productDataResponse.setRealOrderNum(realOrderNum);
                productDataResponse.setPayOrderNum(sqlProductData.getPayOrderNum());
                productDataResponse.setUnPayOrderNum(sqlProductData.getUnPayOrderNum());
                productDataResponse.setTotalOrderNum(sqlProductData.getTotalOrderNum());
                productDataResponse.setOrderRatio(orderRatio);
                productDataResponse.setPayRatio(payRatio);
                productDataResponse.setActivityType(ActivityTypeNameCode.getName(productDataResponse.getActivityType()));
            }
        }
        return productDataResponseList;
    }

    @Override
    public List<ProductDataResponse> queryProductDataByDay(String platform, String channel, String dayTime) {
        Criteria criteria = this.commonCriteria(platform, channel, null, dayTime);
        List<ProductDataResponse> productDataResponseList = this.aggregateByProductName(criteria, dayTime);
        for (ProductDataResponse item : productDataResponseList) {
            Integer productId = item.getProductId();
            Criteria orderCriteria = this.commonOrderCriteria(productId, platform, channel);
            ProductDataResponse orderResponse = this.aggregateByProductId(orderCriteria, dayTime, item);
            // uv
            Integer uv = item.getUv();
            Integer realOrderNum = orderResponse.getRealOrderNum();
            Integer payOrderNum = orderResponse.getPayOrderNum();
            Integer totalOrderNum = orderResponse.getTotalOrderNum();
            Integer unPayOrderNum = totalOrderNum - payOrderNum;
            // 订单率 实际订单数/uv
            BigDecimal orderRatio = uv == 0 ? BigDecimal.ZERO : BigDecimal.valueOf(realOrderNum)
                    .divide(BigDecimal.valueOf(uv), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
            // 支付率 （支付订单/实际订单数）
            BigDecimal payRatio = realOrderNum == 0 ? BigDecimal.ZERO : BigDecimal.valueOf(payOrderNum)
                    .divide(BigDecimal.valueOf(realOrderNum), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
            item.setTotalOrderNum(totalOrderNum);
            item.setUnPayOrderNum(unPayOrderNum);
            item.setRealOrderNum(realOrderNum);
            item.setPayOrderNum(payOrderNum);
            item.setOrderRatio(orderRatio);
            item.setPayRatio(payRatio);
            item.setActivityType(ActivityTypeNameCode.getName(item.getActivityType()));
        }
        return productDataResponseList;
    }

    @Override
    public List<RedPacketResponse> findRedPacketList(String startDate, String endDate) {
        List<RedPacketResponse> list = orderStatisticsMapper.findNum1(startDate,endDate);
        List<RedPacketResponse> list2 = orderStatisticsMapper.findNum2(startDate,endDate);
        if (CollectionUtils.isNotEmpty(list) && CollectionUtils.isNotEmpty(list2)){
            Map<Integer,RedPacketResponse> map = list2.stream().collect(Collectors.toMap(RedPacketResponse::getProductId, Function.identity()));
            for (RedPacketResponse item:list){
                RedPacketResponse redPacketResponse = map.get(item.getProductId());
                if (redPacketResponse!=null){
                    item.setNum2(redPacketResponse.getNum2());
                    item.setNum3(redPacketResponse.getNum3());

                    DecimalFormat df = new DecimalFormat("#.##");
                    String rate = df.format(Double.valueOf(item.getNum3()) / Double.valueOf(item.getNum1()));
                    item.setRate(rate);
                }
            }
        }
        return list;
    }


    public void redisConfig(String dateType, String platform, ProductStatisticsResponse productStatisticsResponse) {
        if (!DateTypeEnums.DAY.name().equals(dateType)) {
            redisService.set(RedisConstants.PRODUCT_STATISTICS + platform + dateType, JSONObject.toJSONString(productStatisticsResponse));
            redisService.expire(RedisConstants.PRODUCT_STATISTICS + platform + dateType, CalendarUtil.getSecondsNextEarlyMorning());
        }
    }


    /**
     * 通用查询
     *
     * @param platform
     * @param channel
     * @return
     */
    public Criteria commonCriteria(String platform, String channel, String dateType, String dateTime) {
        Criteria criteria = Criteria.where("dayTime").is(dateTime);
        if (StringUtils.isNotEmpty(platform)) {
            criteria.and("platform").is(platform);
        }
        if (StringUtils.isNotEmpty(channel)) {
            criteria.and("channel").is(channel);
        }
        return criteria;
    }


    public List<Map> aggregateByUuid(Criteria criteria, String collectionType) {
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("uuid").count().as("count").last("uuid").as("uuid")
        );
        AggregationResults<Map> results = null;
        if (CollectionConstant.PAGE.equals(collectionType)) {
            results = mongoTemplate.aggregate(aggregation, MongoCollectionNameConstants.PAGE_STATISTICS, Map.class);
        }
        if (CollectionConstant.PRODUCT.equals(collectionType)) {
            results = mongoTemplate.aggregate(aggregation, MongoCollectionNameConstants.PRODUCT_STATISTICS, Map.class);
        }
        return results.getMappedResults();
    }


    public List<ProductDataResponse> aggregateByProductName(Criteria criteria, String dayTime) {
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("productId", "uuid")
                        .last("productId").as("productId")
                        .last("productName").as("productName")
                        .last("activityType").as("activityType"),
                Aggregation.group("productId").count().as("uv")
                        .last("productName").as("productName")
                        .last("productId").as("productId")
                        .last("activityType").as("activityType")
        );
        return mongoTemplate.aggregate(aggregation, MongoCollectionNameConstants.PRODUCT + dayTime, ProductDataResponse.class)
                .getMappedResults();
    }

    public Map countOrderId(Criteria criteria, String collectionName){
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("orderInfoId"),
                Aggregation.count().as("orderCount")
        );
        return mongoTemplate.aggregate(aggregation, collectionName, Map.class).getUniqueMappedResult();
    }
    /**
     * 根据商品id进行查询
     */
    public ProductDataResponse aggregateByProductId(Criteria criteria, String dayTime, ProductDataResponse productDataResponse) {
        Query query = new Query();
        query.addCriteria(criteria);

        BigDecimal price = null;
        BigDecimal costPrice = null;
        OrderStatisticsResponse one = mongoTemplate.findOne(query, OrderStatisticsResponse.class, MongoCollectionNameConstants.ORDER + dayTime);
        if (null != one) {
            // 成本价
            costPrice = one.getCostPrice();
            // 售价
            price = one.getPrice();
        }
        // 总订单
        //long totalOrderNum = mongoTemplate.count(query, MongoCollectionNameConstants.ORDER + dayTime);
        Map map = countOrderId(criteria, MongoCollectionNameConstants.ORDER + dayTime);

        Integer totalOrderNum = null == map ? 0 : (Integer) map.get("orderCount");

        // 实际订单
//        Query realOrderQuery = new Query().addCriteria(criteria)
//                .addCriteria(criteria.and("isAddress").is(1));
//        long realOrderNum = mongoTemplate.count(realOrderQuery, MongoCollectionNameConstants.ORDER + dayTime);

        criteria.and("isAddress").is(1);
        map = countOrderId(criteria, MongoCollectionNameConstants.ORDER + dayTime);
        Integer realOrderNum = null == map ? 0 : (Integer) map.get("orderCount");

        // 支付订单
        criteria.and("isPay").is(1);
//        Query payOrderQuery = new Query().addCriteria(criteria)
//                .addCriteria(criteria.and("isPay").is(1));
//        long payOrderNum = mongoTemplate.count(payOrderQuery, MongoCollectionNameConstants.ORDER + dayTime);
        map = countOrderId(criteria, MongoCollectionNameConstants.ORDER + dayTime);
        Integer payOrderNum = null == map ? 0 : (Integer) map.get("orderCount");
        productDataResponse.setPrice(price);
        productDataResponse.setCostPrice(costPrice);
        productDataResponse.setRealOrderNum( realOrderNum);
        productDataResponse.setTotalOrderNum(totalOrderNum);
        productDataResponse.setPayOrderNum(payOrderNum);
        return productDataResponse;
    }

    /**
     * 订单查询通用Criteria
     */
    public Criteria commonOrderCriteria(Integer productId, String platform, String channel) {
        Criteria criteria = Criteria.where("productId").is(productId);
        if (StringUtils.isNotEmpty(platform)) {
            criteria.and("platform").is(platform);
        }
        if (StringUtils.isNotEmpty(channel)) {
            criteria.and("channel").is(channel);
        }
        return criteria;
    }


    /**
     * 获取时间
     *
     * @return
     */
    public DateResponse getTime(String dateType, String platform, String channel, String dayTime) {
        DateResponse dateResponse = new DateResponse();
        TimeInterfaceFun timeInterfaceFun = TimeEnums.getTimeEnums(dateType);
        TimeDto timeDto = timeInterfaceFun.getTimeDto(dayTime);
        dateResponse.setStartTime(timeDto.getStartTime());
        dateResponse.setEndTime(timeDto.getEndTime());
        dateResponse.setStartDateRange(timeDto.getDay());
        dateResponse.setPlatform(platform);
        dateResponse.setChannel(channel);
        return dateResponse;
    }

    /**
     * 商品销量排行
     *
     * @return
     */
    public List<ProductResponse> getProductRank(DateResponse time) {
        return orderStatisticsMapper.getOrderRank(time);
    }

    /**
     * 成交额和销售量
     *
     * @return
     */
    public List<Object> getOrderData(String dateType, DateResponse dateResponse) {
        dateResponse.setDateType(dateType);
        return Optional.ofNullable(this.getData(dateResponse, DateTypeEnums.getDateDayNum(dateType)))
                .orElse(new ArrayList<>());
    }


    /**
     * 专区销售量
     */
    public List<ProductSpecialResponse> getProductSpecialList(DateResponse dateResponse) {
        return orderStatisticsMapper.getProductSpecialList(dateResponse);
    }

    /**
     * 专区点击量
     */
    public List<SpecialClickResponse> getProductSpecialClickNum(DateResponse dateResponse) {
        List<String> specialCode = SpecialClickEnums.getSpecialCodeList();
        List<SpecialClickResponse> specialClickList = new ArrayList<>();
        for (String item : specialCode) {
            SpecialClickResponse specialClickResponse = new SpecialClickResponse();
            Long num = this.specialSearch(item, dateResponse);
            specialClickList.add(specialClickResponse.setSpecialCode(item)
                    .setClickNum(num)
                    .setSpecialName(SpecialClickEnums.getSpecialName(item)));
        }
        return specialClickList;
    }

    /**
     * 通用专区点击查询
     */
    public Long specialSearch(String specialCode, DateResponse dateResponse) {
        Criteria criteria = this.commonCriteria(dateResponse.getPlatform(), dateResponse.getChannel(), dateResponse.getDateType(), null)
                .and("clickType").is(specialCode);
        return mongoTemplate.count(new Query(criteria), ClickStatistics.class);
    }


    /**
     * 获取数据
     *
     * @return
     */
    public List<Object> getData(DateResponse dateResponse, Integer num) {
        List<OrderStatisticResponse> orderDataList = null;
        if (DateTypeEnums.DAY.name().equals(dateResponse.getDateType())) {
            orderDataList = orderStatisticsMapper.getOrderDataByHour(dateResponse);
        } else {
            orderDataList = orderStatisticsMapper.getOrderDataByWeekAndMonth(dateResponse);
        }
        List<Object> orderList = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            Map orderMap = new HashMap<>();
            orderMap.put("timeCount", i + 1);
            orderMap.put("totalMoney", 0.00);
            orderMap.put("payOrderNum", 0);
            if (DateTypeEnums.DAY.name().equals(dateResponse.getDateType())) {
                String time = String.valueOf(i);
                this.commonMap(orderMap, time, orderDataList);
            } else {
                LocalDate time = dateResponse.getStartDateRange().plusDays(i);
                this.commonMap(orderMap, time, orderDataList);
            }
            orderList.add(orderMap);
        }
        return orderList;
    }


    public Map commonMap(Map orderMap, Object time, List<OrderStatisticResponse> orderDataList) {
        orderMap.put("time", time + "");
        OrderStatisticResponse orderStatisticResponse = orderDataList.stream()
                .filter(item -> item.getTime().equals(time.toString()))
                .findAny().orElse(null);
        if (orderStatisticResponse != null) {
            orderMap.put("totalMoney", orderStatisticResponse.getTotalMoney());
            orderMap.put("payOrderNum", orderStatisticResponse.getPayOrderNum());
        }
        return orderMap;
    }


    public ResponseEntity<GroupByResults> getPageCount() {
        GroupBy groupBy = new GroupBy("uuid").initialDocument("{ collection: [] }")
                .reduceFunction("function(doc, pre) {" +
                        "pre.collection.push({id:doc._id.str, uuid:doc.uuid, pathCode:doc.pathCode});" +
                        "}");
        GroupByResults groupByResults = mongoTemplate.group("page_statistics", groupBy, PageStatistics.class);
        ResponseEntity<GroupByResults> resultEntity = new ResponseEntity<>(groupByResults, HttpStatus.OK);
        return resultEntity;
    }

    public static void main(String[] args) {
        DecimalFormat df = new DecimalFormat("#.##");
        String rate = df.format(Double.valueOf(3) / Double.valueOf(42));
        rate = String.valueOf(Double.valueOf(rate) *100);
        System.out.println(rate);
//          double b = 21.0/186;
//          System.out.println(b);
    }


}
